/* eslint-disable no-self-compare */
/*
代理Set和Map
*/

import {
  track,
  trigger,
  ITERATE_KEY,
  MAP_KEY_ITERATE_KEY,
  TriggerType,
  effect,
} from './reactive-system'

// ================== 1. Set size ==================
/* const s = new Set([1, 2, 3])
const p = new Proxy(s, {
  get(target, key, receiver) {
    if (key === 'size') {
      // 读取size时， receriver是代理对象
      return Reflect.get(target, key, target)
    }
    return target[key].bind(target)
  },
})
console.log(p.size) // 代理set对象 proxy对象上没有size，要在get的时候处理
p.delete(3) // p上没有delete函数，需要在get时处理
console.log(p.size) */

// 封装createReactive函数
const reactiveMap = new Map()

// 包装函数，reactive(val)
const wrap = (val) => (typeof val === 'object' ? reactive(val) : val)

function reactive(obj) {
  const existedProxy = reactiveMap.get(obj)
  if (existedProxy) return existedProxy
  const proxy = createReactive(obj)
  reactiveMap.set(obj, proxy)
  return proxy
}
// 重写Set / Map 的方法
const mutableInstrumentations = {
  // Set 方法重写
  add(key) {
    const target = this.raw
    if (target.has(key)) return this
    const res = target.add(key)
    trigger(target, key, TriggerType.ADD)
    return reactive(res) // Set.prototype.add方法返回值为调用的set, 为了链式调用方便
  },
  delete(key) {
    const target = this.raw
    const res = target.delete(key)
    if (res) {
      trigger(target, key, TriggerType.DELETE)
    }
    return res
  },
  // Map 方法重写
  get(key) {
    const target = this.raw
    const had = target.has(key)
    track(target, key)
    if (had) {
      const res = target.get(key)
      return typeof res === 'object' ? reactive(res) : res
    }
  },
  set(key, val) {
    const target = this.raw
    const had = target.has(key)
    const oldVal = target.get(key)
    // 如果val是响应式数据(proxy)， 那么我们set应该取它的原始值, 这么做是为了避免污染原始数据，即原始数据操作没有响应式， 要响应式就必须要操作响应式数数
    const newVal = val.raw || val
    target.set(key, newVal)
    if (!had) {
      trigger(target, key, TriggerType.ADD)
    } else if (oldVal !== val || (oldVal === oldVal && val === val)) {
      trigger(target, key, TriggerType.SET)
    }
    return this
  },
  forEach(callback, thisArg) {
    const target = this.raw
    track(target, ITERATE_KEY)
    target.forEach((v, k) => {
      callback.call(thisArg, wrap(v), wrap(k), this)
    })
  },
  [Symbol.iterator]: iterationMethod,
  entries: iterationMethod, // for ... of map.entries()
  values: keyOrValueIterationMethod('values'),
  keys: keyOrValueIterationMethod('keys'),
}

// for ... of map[.entries()] 迭代器函数， 返回自定义的迭代器
function iterationMethod() {
  const target = this.raw
  const iter = target[Symbol.iterator]()
  track(target, ITERATE_KEY) // track 迭代器
  return {
    // 迭代器协议
    next() {
      const { value, done } = iter.next()
      return {
        value: value ? [wrap(value[0]), wrap(value[1])] : value, // value可能为undefined
        done,
      }
    },
    // 可迭代协议
    [Symbol.iterator]() {
      return this
    },
  }
}

/**
 *'keys()'或者'values()'返回不同的迭代器
 * @param {'keys'|'values'} type
 * @returns
 */
function keyOrValueIterationMethod(type) {
  return function () {
    const target = this.raw
    const iter = type === 'values' ? target.values() : target.keys()
    track(target, type === 'values' ? ITERATE_KEY : MAP_KEY_ITERATE_KEY)
    return {
      next() {
        const { value, done } = iter.next()
        return {
          value: wrap(value),
          done,
        }
      },
      [Symbol.iterator]() {
        return this
      },
    }
  }
}

function createReactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      if (key === 'raw') return target
      if (key === 'size') {
        track(target, ITERATE_KEY)
        return Reflect.get(target, key, target)
      }
      return mutableInstrumentations[key]
    },
  })
}

// ====================== 2. Set的add/delete ======================
/* const p = reactive(new Set([1, 2, 3]))
effect(() => {
  console.log(p.size)
})
p.add(4).add(1).add(5).delete(2) */

// ====================== 3. Map set/get ======================
/* const p = reactive(new Map([['key', 1]]))
effect(() => {
  console.log(p.get('key'))
})
p.set('key', 2) */

// 避免污染原始数据
/* const m = new Map()
const p1 = reactive(m)
const p2 = reactive(new Map())
p1.set('p2', p2)
effect(() => {
  console.log(m.get('p2').size)
})

m.get('p2').set('foo', 1) // m是原始数据，m.get('p2')拿到的应该也是原始数据，原始数据不应该具有响应式， effectFn不应该执行两次 */

// ====================== 4. forEach ======================
// 基本场景
/* const p = reactive(new Map([[{ key: '我是key' }, { value: '我是value' }]]))
effect(() => {
  console.count('effect forEach')
  p.forEach((value, key) => {
    console.log(key, value)
  })
})

p.set({ key: 2 }, { value: 2 }) */

// value为响应式数据的场景
/* const key = { key: 1 }
const value = new Set([1, 2, 3])
const p = reactive(new Map([[key, value]]))

effect(() => {
  p.forEach(function (value, key) {
    console.log(value.size)
  })
})
p.get(key).delete(1) */

// map的forEach即关心value, 也关心key
/* const p = reactive(new Map([['key', 1]]))
effect(() => {
  p.forEach((value, key) => {
    console.log(value)
  })
})
p.set('key', 2) // 修改值, 要出发上面的forEach effectFn
 */

// ====================== 5. 迭代器 ======================
/* const p = reactive(
  new Map([
    ['key1', 'val1'],
    ['key2', 'val2'],
  ])
)

effect(() => {
  console.count('iterator effectFn')
  // for..of.. 调用的是[get] [Symbol.iterator]
  for (const [key, value] of p) {
    console.log(key, value)
  }
})

effect(() => {
  console.count('entries effectFn')
  // for..of.. 调用的是[get] [Symbol.iterator]
  for (const [key, value] of p.entries()) {
    console.log(key, value)
  }
})

effect(() => {
  console.count('keys effectFn')
  for (const key of p.keys()) {
    console.log(key)
  }
})

effect(() => {
  console.count('values effectFn')
  for (const val of p.values()) {
    console.log(val)
  }
})

p.set('key3', 'val3')
 */

// ====================== 避免不必要的keys更新 ======================
const p = reactive(new Map([['key1', 'val1']]))
effect(() => {
  for (const key of p.keys()) {
    console.log(key)
  }
})
p.set('key1', 'new Val') // set已有值，不需要执行keys的effectFn
